vimfandomcom-20200223-history
Searching for files in a directory listing
Occasionally I use Vim to search for filenames in a directory listing rather than relying on windows search or some other search tool that might not be available at the time. This has the advantage of allowing me to use Vim commands to filter and modify the results as needed. The main drawback with this approach is that dos directory listings don't list the filename and path on a single line thus requiring more advanced searches to be constructed to find anything. The following commands will take a dos directory listing created with dir /s and turn it into a line by line listing containing the full path to each file or directory. This tip is of primary relevance to windows/dos users but the ideas outlined might be useful to users of other systems. Desired result Turn this: Directory of C:\WINNT 20/08/2010 09:31 AM . 20/08/2010 09:31 AM .. 30/08/2010 09:13 AM 0 0.log 29/12/2006 12:31 AM 19,569 003199_.tmp 03/06/2008 07:04 PM addins 30/07/2010 08:43 PM AppPatch 04/08/2004 10:00 PM 1,272 Blue Lace 16.bmp 24/02/2009 03:28 PM bruce lee! 04/08/2004 10:00 PM 82,944 clock.avi 30/07/2010 08:16 PM 373 cmsetacl.log 04/08/2004 10:00 PM 17,062 Coffee Bean.bmp 19/08/2010 09:17 PM 324,805 comsetup.log into this: C:\WINNT\ C:\WINNT\0.log C:\WINNT\003199_.tmp C:\WINNT\Blue Lace 16.bmp C:\WINNT\clock.avi C:\WINNT\cmsetacl.log C:\WINNT\Coffee Bean.bmp C:\WINNT\comsetup.log C:\WINNT\addins\ C:\WINNT\AppPatch\ C:\WINNT\AppPatch\acadproc.dll C:\WINNT\AppPatch\acgenral.dll C:\WINNT\AppPatch\aclayers.dll Getting the directory listing To get a dos directory listing of your c drive try typing: c:\>dir /s > dirlist.txt to edit this listing in Vim: vim dirlist.txt now we can reformat the listing to be line based: " remove blank lines g/^\s*$/d " remove header g/^ Volume/d " remove footer g/^\s\+Total Files Listed/.,+2d " remove directory entries g/\s \s/d " replace file entries with filename followed by delimiter %s/^\d\d\/\d\d\/\d\d\d\d \d\d:\d\d APM \s*0-9,\+ /``````:::::: " replace backslash path separators with forward slashes %s/\\/\//g " create a copy of the directory path and append /. One copy is left alone the other gets prepended to all files inside. %s/^ Directory of \(.*\)\s*$/\1\/\r@@\1\/@@/ " join together files in a directory g/^@@/,/File(s)/j " remove folder info at end %s/ 0-9,\+ File(s)\s\+0-9,\+ bytes\s*$// " prepend path to each file %s/^@@\(.\{-}\)@@\(.*\)/\=substitute(submatch(2),"::::::",submatch(1),"g")/ " split into separate lines %s/``````/\r/g " remove blank lines g/^\s*$/d " convert forward slashes to backslashes %s/\//\\/g " remove blanks at line end %s/\s*$ if you put this into a file and call it reformat.vim you can then source it on a directory listing using: :so reformat.vim Searching the result Now if you want to see only doc files you can do: :v/\c\.doc$/d Or you want to show all the folders which contain .vim files: :v/\c\.vim$/d :%s/\c\\^\\\+\.vim$// :sort u Further processing To take this one step further we can now convert this filtered directory listing into a batch file to operate on each line. Delete each file: :%s/^\(.*\)/del "\1"/ Rename each file: %s/^\(.*\)\\\(^\\\+\)\.txt$/rename "\1\\\2.txt" "\1\\\2.txt.old"/ Now save as a dos batch file :w renamefiles.bat and run. Of course don't run this unless you are **absolutely sure** the commands do what you want. There are some edge cases that will cause the reformatting code to trip up if you have strange characters in your filenames. Comments On Unix you can use find /some/directory | vim -. Windows users could install GnuWin to obtain a version of find utility to use the same procedure. The GnuWin package is updated from an earlier and obsolete UnxUtils package. :I refactored the above comment to remove the link to UnxUtils which I am fairly sure is obsolete. See here for info on signing comments. JohnBeckett 10:16, September 2, 2010 (UTC) ---- A simple Windows procedure to list file paths is to enter the following at command prompt, replacing DIRECTORY with the path of the directory you want: xcopy DIRECTORY\* \ /s /h /l That produces a list of the full path of each file in and under DIRECTORY. If you replace the * with *.c the list will include only *.c files. JohnBeckett 10:16, September 2, 2010 (UTC) :Wow, this is really slick, and gets rid of most of the scripting in the tip. Thanks! I was going to mention the /b option for the dir command, but this is even better! --Fritzophrenic 13:39, September 2, 2010 (UTC) ---- Great info about getting directory listings for dos. I didn't know of either of these methods and I can't think why I didn't try the /b switch. I would not have written the vim script if I was aware of these alternatives and saved myself some time. It may be interesting to note that the 3 mentioned methods of generating a dir listing all differ slightly in what they produce and one or other may be better suited depending on what you intend to do with the output. The xcopy command does not produce directory entries and gives a one line file summary at the end. dir /s /b lists directories and lists them first but doesn't distinguish between a directory and a file. dir /s and vim script produces a directory listing and marks directories with a trailing slash. I have a feeling it might not list empty directories and it also produces double slashes for the drive root, if included - probably an easy fix. A simple modification to the vim script can be made to include file sizes at the end of each line. That way you could easily list the 10 biggest movie files on your disk and delete or move them to another location. (code pending). --MisterW 23:29, September 2, 2010 (UTC) :There is also the /a option: dir /s /b /a-d is files only ("attributes not directory"), and dir /s /b /ad is directories only. JohnBeckett 05:23, September 3, 2010 (UTC) ---- The following script (which could be in your vimrc) defines a command to list files in a scratch buffer. There are several good tools for finding files and navigating (for example, :e . explores the current directory). However, it can be useful to generate a list of file paths which you can edit. With the cursor in a file path, press gf to jump to that file. " Create a scratch buffer with a list of files (full path names). " Argument is a specification like '*.c' to list *.c files (default is '*'). " Can use '*.ch' to find *.c and *.h (see :help wildcard). " If command uses !, list includes matching files in all subdirectories. " If filespec contains a slash or backslash, the path in filespec is used; " otherwise, start searching in directory of current file. function! s:Listfiles(bang, filespec) if a:filespec =~ '/\\' " if contains path separator (slash or backslash) let dir = fnamemodify(a:filespec, ':p:h') let fnm = fnamemodify(a:filespec, ':p:t') else let dir = expand('%:p:h') " directory of current file let fnm = a:filespec endif if empty(fnm) let fnm = '*' endif if !empty(a:bang) let fnm = '**/' . fnm endif let files = filter(split(globpath(dir, fnm), '\n'), '!isdirectory(v:val)') echo 'dir=' dir ' fnm=' fnm ' len(files)=' len(files) if empty(files) echo 'No matching files' return endif new setlocal buftype=nofile bufhidden=hide noswapfile call append(line('$'), files) 1d " delete initial empty line " sort i " sort, ignoring case endfunction command! -bang -nargs=? Listfiles call s:Listfiles(, '') Usage examples (these use forward slashes which work on Unix and Windows systems): :Listfile " list all files in directory of current file :Listfile! " same, but also include subdirectories :Listfile! *.c " list all *.c files in tree of current file :Listfile /my/path/*.c " list *.c in given path :Listfile! /my/path/*.c " list *.c in given tree Depending on what commands you have defined on your system, you may be able to abbreviate :Listfile to simply :L. ''Todo Could make a buffer-local mapping so that pressing Enter will execute gf on the current line. Use command 0vg_gf to select the current line before the gf so that it works on paths that contain spaces. I have just created the above script. It should work on all Vim 7 systems, and might be worth mentioning in a tip, perhaps here. If anyone has some comments, please add them below. JohnBeckett 10:16, September 2, 2010 (UTC) :Looks pretty interesting, though it probably needs some implementation comments. I'm not so sure about the Enter mapping. It could be useful but I'm worried if we present it without a way to do the same thing in netrw people will just use it without ever knowing about builtin features. I'm pretty sure it's possible to filter the netrw window for a certain file name pattern. I do know it's possible to set it up so you have a tree view instead of a single directory at a time. Something to look into. --Fritzophrenic 13:39, September 2, 2010 (UTC) :Very useful John. --MisterW 23:34, September 2, 2010 (UTC)